package com.yudean.esb.util;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;

/**
 * 安全工具类，提供一个生成全局UUID，以及一个简单的封装用户信息的实现
 *
 * @author : DL<duanlei@gdyd.com>
 * @version : 1.0
 */
public class SecurityTools {

    //加密所用的SALT 生成地址为：https://api.wordpress.org/secret-key/1.1/salt/
    private final static String SALT = "YPBoEdRTuPB/+n)[E-D.V:4rqe!AjT1|bT5-AMPu+$k#S~Jb<HX:&{2dA[/*XZdS";

    //16进制映射
    private static String HEX = "0123456789ABCDEF";

    /**
     * 生成一个包含用户信息的token
     *
     * @param userID     用户信息标示
     * @param expireTime token有效期，秒为单位
     * @return
     */
    public static String wrapUserInfo(String userID, long expireTime) {
        if (expireTime < 0) expireTime = 1000;
        StringBuilder sb = new StringBuilder();
        long time = System.currentTimeMillis() + expireTime * 1000;
        sb.append(userID).append("#").append(time);
        String token = encrypt(sb.toString());
        return token;
    }

    /**
     * 将用户信息解开，解开后为一个长度为2的数组
     * 0---用户ID
     * 1---到期时间（long,SystemCurrentMillis）
     *
     * @param token
     * @return
     */
    public static String[] unWrapUserInfo(String token) {
        if (token != null && !"".equals(token)) {
            try {
                String decrypted = decrypt(token);
                return decrypted.split("#");
            } catch (Exception e) {
            }
        }
        return null;
    }

    /**
     * 根据旧ID生成新的token
     *
     * @param oldToken
     * @param expireTime
     * @return
     */
    public static String refreshToken(String oldToken, long expireTime) {
        if (expireTime < 0) expireTime = 1000;
        if (oldToken == null || "".equals(oldToken)) {
            return null;
        }
        String[] userInfo = unWrapUserInfo(oldToken);
        if (userInfo == null) {
            return null;
        }
        String userID = userInfo[0];
        return wrapUserInfo(userID, expireTime);
    }

    /**
     * 用来生成系统共用的UUID，出于安全的考虑，增加了一位的安全码
     *
     * @return
     */
    public static String UUID() {
        String hex = IDGenerator.getHex().toUpperCase();
        char securityCode = buildSecurityCode(hex, 16);
        return hex + securityCode;
    }

    /**
     * 验证是否是合法的UUID
     *
     * @param uuid
     * @return
     */
    public static boolean verifyUUID(String uuid) {
        if (uuid.length() != 33) {
            return false;
        }
        String fisrt32 = uuid.substring(0, 32);
        char lastOne = uuid.charAt(32);
        char securityCode = buildSecurityCode(fisrt32, 16);
        if (lastOne == securityCode)
            return true;
        return false;
    }

    /**
     * 获得长整型的唯一序列
     *
     * @return
     */
    public static String getNumberic() {
        Long num = IDGenerator.getAtomicCounter();
        return String.valueOf(num);
    }

    /**
     * 提供一个方法，要求提供一个源字符串
     *
     * @param src
     * @return
     */
    public static String encrypt(String src) {
        return CryptUtils.encrypt(src, SecurityTools.SALT);
    }

    /**
     * 还原原始的Token内容，如果不是由系统加密的token或者是非法的，就将异常抛出
     * 由调用者处理
     *
     * @param token
     * @return
     * @throws Exception token非法。
     */
    public static String decrypt(String token) throws Exception {
        if (token != null && !"".equals(token)) {
            return CryptUtils.decrypt(token, SecurityTools.SALT);
        }
        return null;
    }

    /**
     * 生成最后一位验证码
     *
     * @param str
     * @param size 16 或者 10
     * @return
     */
    private static char buildSecurityCode(String str, int size) {
        if (str == null || "".equals(str))
            return 0;
        if (size != 10 || size != 16) size = 16;

        byte[] bytes = str.getBytes();
        int result = 0;
        for (byte one : bytes) {
            result += (one & 0xff);
        }
        int mod = result % size;
        char securityCode = HEX.charAt(mod);
        return securityCode;
    }


    public static void main(String[] args) throws Exception {
        //System.out.println(decrypt("0EE481678267D6E45B564F85BE05821ABF0210BC67411292"));
        //System.out.println(encrypt("1234567aB@"));      //692E2A2990AE5DBF52F8A1F99641F710
        System.out.println(encrypt("BiyKw43T"));        //B33ED4B6C35FF3066B72776738B35E1C
        //System.out.println(decrypt("fa4bee3687ce07e05a19fcac9ed9c5b46a041be5".toUpperCase()));

        /*System.out.println(verifyUUID("85D3C769D0F94F90B1594048D33656357"));
        System.out.println(new Date(812121219));
        // System.out.println(wrapUserInfo("ZSR_EAM", 312121212));
        String[] codes = new String[]{"GQL",};
        int i = 1;
        for (String code : codes) {
            String LOGIN_CODE = code;
            code = wrapUserInfo(LOGIN_CODE, 812121219);
            getNumberic();
            String uuid = UUID();
            if (!verifyUUID(uuid)) {
                System.out.println("oops!");
            }

            System.out.println(LOGIN_CODE + ": " + code);
            for (String s : unWrapUserInfo(code)) {
                System.out.println(s);
            }
            int id = 120 + (i++);*/
            /*System.out.println("insert into sys_appuser_org (ID, COMP_CODE, LOGIN_ID) values (" +
                    id +
                    ", " +
                    "'" + code + "'" +
                    ", " +
                    id +
                    ");");


            System.out.println("insert into sys_appuser (LOGIN_ID, LOGIN_CODE, LOGIN_NAME, PASSWORD, EMAIL, TEL, ADDRESS, STATE) " +
                    "values (" +
                    "17" +
                    ", " +
                    "'" + LOGIN_CODE + "', " +
                    "'财务共享中心" + code + "', " +
                    "'" + uuid + "', " +
                    "'', " +
                    "null, " +
                    "null, " +
                    "1" +
                    ");");*/
        }

}

/**
 * 加密工具类的实现。
 */
class CryptUtils {

    private final static String DES = "DES";
    private static String defaultKey = "";

    // 这里的代码段主要是为了加密用的key大于8位
    static {
        String strKey = "$pNtFSg.nm1$-~-9HQ#^q}t1&:K~=:qxqD3#(FSSFTaH?}A.F{LVEwf>rL|Ib2=@";//测试用
        int len = strKey.length();
        if (len < 8) {
            for (int i = len; i < 8; i++) {
                strKey += "$";
            }
        }
        defaultKey = strKey;
    }

    /**
     * 加密，使用的是本身提供的加密串
     *
     * @param src
     * @return
     */
    public static String encrypt(String src) {
        return encrypt(src, defaultKey);
    }

    /**
     * 解密，使用的是默认提供的解密串
     *
     * @param src
     * @return
     * @throws Exception
     */
    public static String decrypt(String src) throws Exception {
        return decrypt(src, defaultKey);
    }

    /**
     * 数据解密
     *
     * @param data
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public final static String decrypt(String data, String key)
            throws Exception {
        return new String(decrypt(hex2byte(data.getBytes()), key.getBytes()));
    }

    /**
     * 数据加密
     *
     * @param data
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public final static String encrypt(String data, String key) {
        if (data != null)
            try {
                return byte2hex(encrypt(data.getBytes(), key.getBytes()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        return null;
    }

    /**
     * 加密
     *
     * @param src 数据源
     * @param key 密钥，长度必须是8的倍数
     * @return 返回加密后的数据
     * @throws Exception
     */
    private static byte[] encrypt(byte[] src, byte[] key)
            throws RuntimeException {
        // DES算法要求有一个可信任的随机数源
        try {
            SecureRandom sr = new SecureRandom();
            // 从原始密匙数据创建DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key);
            // 创建一个密匙工厂，然后用它把DESKeySpec转换成
            // 一个SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey securekey = keyFactory.generateSecret(dks);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance(DES);
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
            // 现在，获取数据并加密
            // 正式执行加密操作
            return cipher.doFinal(src);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解密
     *
     * @param src 数据源
     * @param key 密钥，长度必须是8的倍数
     * @return 返回解密后的原始数据
     * @throws Exception
     */
    private static byte[] decrypt(byte[] src, byte[] key)
            throws RuntimeException {
        try {
            // DES算法要求有一个可信任的随机数源
            SecureRandom sr = new SecureRandom();
            // 从原始密匙数据创建一个DESKeySpec对象
            DESKeySpec dks = new DESKeySpec(key);
            // 创建一个密匙工厂，然后用它把DESKeySpec对象转换成
            // 一个SecretKey对象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
            SecretKey securekey = keyFactory.generateSecret(dks);
            // Cipher对象实际完成解密操作
            Cipher cipher = Cipher.getInstance(DES);
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
            // 现在，获取数据并解密
            // 正式执行解密操作
            return cipher.doFinal(src);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 二行制转字符串
     *
     * @param b
     * @return
     */
    private static String byte2hex(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toUpperCase();
    }

    private static byte[] hex2byte(byte[] b) {
        if ((b.length % 2) != 0)
            throw new IllegalArgumentException();
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);
            b2[n / 2] = (byte) Integer.parseInt(item, 16);
        }
        return b2;
    }
}
